/**
 *
 * \file CButton.cpp
 * \brief This file contains the CButton class implementation.
 * \author Vladimir Klubkov (aka marked)
 *
 * Last changes date:
 * \date 15.03.2011
 *
 * Copyright 2011 Vladimir Klubkov <marked-one@yandex.ru>
 *
 * This file is part of OOXlib.
 *
 * OOXLib is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * OOXlib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with OOXlib; if not, you can try to find  it here:
 * http://www.gnu.org/licenses/gpl.txt
 * or write to the Free Software Foundation, Inc., 59 Temple Place,
 * Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "../include/CButton.h"

namespace OOXlib {

/**
 * Constructor. Calls the base-class constructor.
 * \param File Source code file name.
 * \param iLine Source code line.
 * \param Function Function name.
 */
CButton::CDeleteWindowException::CDeleteWindowException(const ::std::string &File, const int iLine, const ::std::string &Function)
: CException(File, iLine, Function, "Can't send the WM_DELETE_MESSAGE. The XSendEvent function failed.") {}
/**
 * Constructor.
 * \param pXServerConnection A pointer to the CXServerConnection object.
 * \param pScreen Pointer to the CScreen object.
 * \param pParentWindow Parent window object.
 * \param Rectangle Window rectangle (contains coordinates of the left-top corner, width and height).
 * \param pEventHandler Pointer to the CEventHandler object.
 * \param ButtonText Text to draw on the button.
 * \param pBackgroundColor Pointer to the CColor bject, which contains the background color.
 * \param pTextColor Pointer to the CColor bject, which contains the text color.
 * \param iHorizontalOffset Horizontal offset - default margins/free space betweeen elements.
 * \param iVerticalOffset Vertical offset - default margins/free space betweeen elements.
 */
CButton::CButton(const CXServerConnection *pXServerConnection, const CScreen *pScreen, IWindow *pParentWindow, const CUInt2dRectangle &Rectangle, const IEventHandler *pEventHandler,
                 const ::std::string &ButtonText, const CColor *pBackgroundColor, const CColor *pTextColor, const unsigned int iHorizontalOffset, const unsigned int iVerticalOffset)
: CSimpleWindow(pXServerConnection, pScreen, pParentWindow, Rectangle, NoEventMask, NULL, ::std::string(), "Button", ::std::string(), pBackgroundColor, 0, false, true),
m_pGC(NULL), m_Text(ButtonText), m_pTextColor(pTextColor), m_iHorizontalOffset(iHorizontalOffset), m_iVerticalOffset(iVerticalOffset), m_bIsButtonPressed(false), m_bIsMouseOver(false) {
    // Create the graphics context.
    m_pGC = new CGraphicsContext(m_pXServerConnection, this);
    // Set text and compute the text rectangle.
    m_pGC->SetText(m_Text);
    m_Rectangle = m_pGC->ComputeTextRectangle();
    unsigned int parentWidth = static_cast<unsigned int>(m_pParentWindow->GetSize().GetX());
    unsigned int parentHeight = static_cast<unsigned int>(m_pParentWindow->GetSize().GetY());
    unsigned int rectangleWidth = m_Rectangle.GetWidth() + 2 * m_iHorizontalOffset;
    unsigned int rectangleHeight = m_Rectangle.GetHeight() + 2 * m_iVerticalOffset;
    // Cmpute the rectangle position
    unsigned int rectanglePosX = 0;
    unsigned int rectanglePosY = 0;
    if(rectangleWidth < parentWidth) {
        rectanglePosX = Rectangle.GetPositionX();
    }
    if(rectangleHeight < parentHeight) {
        rectanglePosY = Rectangle.GetPositionY();
    }
    // Set the new recatngle.
    m_Rectangle.Set(rectanglePosX, rectanglePosY, rectangleWidth, rectangleHeight);
    delete m_pGC;
    // Destroy window.
    Destroy();
    // Create new window.
    Create(pScreen, pParentWindow, m_Rectangle, ExposureMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask  | SubstructureNotifyMask, pEventHandler, ::std::string(), "Button",
           ::std::string(), pBackgroundColor, 0, false, true);
    // Create graphics context.
    m_pGC = new CGraphicsContext(m_pXServerConnection, this);
    // Set text.
    m_pGC->SetText(m_Text);
}
/**
 * Destructor.
 */
CButton::~CButton() {
    delete m_pGC;
}
/**
 * Expose event handler, draws button graphics and text.
 */
void CButton::OnExpose() {
    // Create needed colors.
    CColor black(m_pXServerConnection, m_pScreen, 0, 0, 0);
    CColor white(m_pXServerConnection, m_pScreen,255, 255, 255);
    CColor gray(m_pXServerConnection, m_pScreen,131, 131, 131);
    // Draw button graphics and text.
    if(m_bIsButtonPressed) {
        // Set foreground color to text color.
        if(NULL == m_pTextColor) {
            m_pGC->SetForegroundColor(&black);
        } else {
            m_pGC->SetForegroundColor(m_pTextColor);
        }
        // Compute the text positions.
        CUInt2dPoint textPosition(m_iHorizontalOffset, m_iVerticalOffset);
        // Draw text.
        m_pGC->DrawText(textPosition);
        // Set foreground color to white.
        m_pGC->SetForegroundColor(&white);
        // Draw white lines.
        CUInt2dLine bottomWhiteLine(1, m_Rectangle.GetHeight() - 1, m_Rectangle.GetWidth() - 1, m_Rectangle.GetHeight() - 1);
        m_pGC->DrawLine(bottomWhiteLine);
        CUInt2dLine rightWhiteLine(m_Rectangle.GetWidth() - 1, 1,  m_Rectangle.GetWidth() -1, m_Rectangle.GetHeight() - 1);
        m_pGC->DrawLine(rightWhiteLine);
        // Set foreground color to black.
        m_pGC->SetForegroundColor(&black);
        // Draw black lines.
        CUInt2dLine topBlackLine(0, 0, m_Rectangle.GetWidth() - 1, 0);
        m_pGC->DrawLine(topBlackLine);
        CUInt2dLine leftBlackLine(0, 0, 0, m_Rectangle.GetHeight() - 1);
        m_pGC->DrawLine(leftBlackLine);
        // Set foreground color to gray.
        m_pGC->SetForegroundColor(&gray);
        // Draw gray lines.
        CUInt2dLine topGrayLine(1, 1, m_Rectangle.GetWidth() - 2, 1);
        m_pGC->DrawLine(topGrayLine);
        CUInt2dLine leftGrayLine(1, 1, 1, m_Rectangle.GetHeight() - 2);
        m_pGC->DrawLine(leftGrayLine);
    } else {
        // Set foreground color to text color.
        if(NULL == m_pTextColor) {
            m_pGC->SetForegroundColor(&black);
        } else {
            m_pGC->SetForegroundColor(m_pTextColor);
        }
        // Compute the text position.
        CUInt2dPoint textPosition(m_iHorizontalOffset - 1, m_iVerticalOffset - 1);
        // Draw text.
        m_pGC->DrawText(textPosition);
        // Set foreground color to black.
        m_pGC->SetForegroundColor(&black);
        // Draw black lines.
        CUInt2dLine bottomBlackLine(0, m_Rectangle.GetHeight() - 1, m_Rectangle.GetWidth() - 1, m_Rectangle.GetHeight() - 1);
        m_pGC->DrawLine(bottomBlackLine);
        CUInt2dLine rightBlackLine(m_Rectangle.GetWidth() - 1, 0,  m_Rectangle.GetWidth() - 1, m_Rectangle.GetHeight() - 1);
        m_pGC->DrawLine(rightBlackLine);
        // Set foreground color to white.
        m_pGC->SetForegroundColor(&white);
        // Draw white lines.
        CUInt2dLine topWhiteLine(0, 0, m_Rectangle.GetWidth() - 2, 0);
        m_pGC->DrawLine(topWhiteLine);
        CUInt2dLine leftWhiteLine(0, 0, 0, m_Rectangle.GetHeight() - 2);
        m_pGC->DrawLine(leftWhiteLine);
        // Set foreground color to gray.
        m_pGC->SetForegroundColor(&gray);
        // Draw gray lines.
        CUInt2dLine bottomGrayLine(1, m_Rectangle.GetHeight() - 2, m_Rectangle.GetWidth() - 2, m_Rectangle.GetHeight() - 2);
        m_pGC->DrawLine(bottomGrayLine);
        CUInt2dLine rightGrayLine(m_Rectangle.GetWidth() - 2, 1, m_Rectangle.GetWidth() - 2, m_Rectangle.GetHeight() - 2);
        m_pGC->DrawLine(rightGrayLine);
    }
}
/**
 * Handles mouse buttons press.
 * \param iButton Button Id.
 * \param iX Cursor X-coordinate.
 * \param iY Cursor Y-coordinate.
 */
void CButton::OnButtonPress(const unsigned int iButton, const int iX UNUSED_PARAM, const int iY UNUSED_PARAM) {
    // If the left mouse button was pressed, set the m_bIsButtonPressed to true and refresh the button window.
    if(Button1 == iButton) {
        m_bIsButtonPressed  = true;
        // XClearWindow can generate BadMatch and BadWindow errors.
        ::XClearWindow(m_pXServerConnection->Get(), m_Window);
        ::XFlush(m_pXServerConnection->Get());
        OnExpose();
    }
}
/**
 * Handles mouse buttons release.
 * \param iButton Button Id.
 * \param iX Cursor X-coordinate.
 * \param iY Cursor Y-coordinate.
 */
void CButton::OnButtonRelease(const unsigned int iButton,const  int iX UNUSED_PARAM, const int iY UNUSED_PARAM) {
    // If the left mouse button was released, set the m_bIsButtonPressed to false, refresh the button window and call the click handler.
    if(Button1 == iButton) {
        m_bIsButtonPressed = false;
        if(m_bIsMouseOver) {
            OnClick();
        }
        // XClearWindow can generate BadMatch and BadWindow errors.
        ::XClearWindow(m_pXServerConnection->Get(), m_Window);
        ::XFlush(m_pXServerConnection->Get());
        OnExpose();
    }
}
/**
 * Called, when the mouse cursor enters the window.
 * \param iX Cursor X-coordinate.
 * \param iY Cursor Y-coordinate.
 */
void CButton::OnMouseEnter(const int iX UNUSED_PARAM, const int iY UNUSED_PARAM) {
    m_bIsMouseOver = true;
}
/**
 * Called, when the mouse cursor exits the window.
 * \param iX Cursor X-coordinate.
 * \param iY Cursor Y-coordinate.
 */
void CButton::OnMouseExit(const int iX UNUSED_PARAM, const int iY UNUSED_PARAM) {
    m_bIsMouseOver = false;
}
/**
 * If button is pressed - exit.
 */
void CButton::OnClick() {
    ::XEvent event;
    event.xclient.type = ClientMessage;
    event.xclient.window = m_Window;
    // XInternAtom can generate BadAlloc and BadValue errors.
    event.xclient.message_type = ::XInternAtom(m_pXServerConnection->Get(), "WM_PROTOCOLS", true);
    event.xclient.format = 32;
    // XInternAtom can generate BadAlloc and BadValue errors.
    event.xclient.data.l[0] = ::XInternAtom(m_pXServerConnection->Get(), "WM_DELETE_WINDOW", false);
    event.xclient.data.l[1] = CurrentTime;
    // XSendEvent can generate BadValue and BadWindow errors.
    if(0 == ::XSendEvent(m_pXServerConnection->Get(), m_Window, False, NoEventMask, &event)) {
        OOXLib_THROW(CDeleteWindowException);
    }
}

} // OOXlib
